#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Data.h"

#include "Class.h"
#include "OrdColl.h"
#include "MemBuf.h"
#include "Bitmap.h"
#include "String.h"
#include "Converter.h"
#include "TypeMatcher.h"
#include "CType.h"
#include "ET_stdio.h"

extern Converter *gEtConverter;
extern TypeMatcher *gEtMatcher;

const Symbol
	cDocCreatorUndef("__UNKNOWN__"),
	cDocTypeUndef("UNDEF"),
	cDocTypeAscii("ASCII"),
	cDocTypeET("ET++OIO");
	//cDocTypeCHeader("CHEADER");
		
extern SmartBitmap gFileIcon;  // bitmap images for file icons

OrdCollection *gConverters;
static OrdCollection *gTypes;

void PrepareTypes()
{
	SafeDelete(gTypes);
	gTypes= new OrdCollection;
}

void EndTypes(Data *data)
{
	if (data && gTypes)
		data->CheckTypes(gTypes);
	SafeDelete(gTypes);
}

//---- Data --------------------------------------------------------------------

NewMetaImpl(Data,RefCounted, (TP(name), T(creator), T(type), T(bits), T(flags),
		T(uniqueid), T(sizehint), T(modtime), TP(classtype), TP(typematcher)));

Data::Data(const char *n)
{
	name= strsave(n ? n : "???");
	classtype= 0;
	type= cDocTypeUndef;
	creator= cDocCreatorUndef;
	typematcher= 0;
	bits= flags= 0;
	uniqueid= 0;
	sizehint= -1;
	modtime= 0;
}

Data::~Data()
{
	SafeDelete(name);
}

void Data::UpdateBit(u_long b, bool val)
{
	if (val)
		SETBIT(bits, b);
	else
		CLRBIT(bits, b);
	SETBIT(flags, b);
}

Class *Data::GetClassType()
{
	UpdateFields(eDataDeepType);
	return classtype;
}

bool Data::IsWritable()
{
	UpdateFields(eDataWrite);
	return TESTBIT(bits, eDataWrite);
}

bool Data::IsReadable()
{
	UpdateFields(eDataRead);
	return TESTBIT(bits, eDataRead);
}

bool Data::IsExecutable()
{
	UpdateFields(eDataExec);
	return TESTBIT(bits, eDataExec);
}

bool Data::IsETFormat()
{
	if (classtype)
		return TRUE;
	UpdateFields(eDataDeepType);
	if (type == cDocTypeET)
		return TRUE;
	if (typematcher && typematcher->GetType() == cDocTypeET)
		return TRUE;
	return FALSE;
}

bool Data::IsAscii()
{
	UpdateFields(eDataType);
	if (typematcher)
		return typematcher->ascii;
	return Type() == cDocTypeAscii;
}

bool Data::IsValid()
{
	UpdateFields(eDataValid);
	return TESTBIT(bits, eDataValid);
}

bool Data::IsUntitled()
{
	UpdateFields(eDataNew);
	return TESTBIT(bits, eDataNew);
}

bool Data::HasContents()
{
	UpdateFields(eDataType);
	return type == cDocTypeDirectory;
}

u_long Data::ModifiedAt()
{
	UpdateFields(eDataTime);
	return modtime;
}

long Data::SizeHint()
{
	UpdateFields(eDataSize);
	return sizehint;
}

const Symbol &Data::Type()
{
	UpdateFields(eDataDeepType);
	return type;
}

void Data::SetType(const Symbol &t)
{
	type= t;
	SETBIT(flags, eDataDeepType);
}

const Symbol &Data::Creator()
{
	UpdateFields(eDataCreator);
	return creator;
}

void Data::SetCreator(const Symbol &crea)
{
	creator= crea;
	SETBIT(flags, eDataCreator);
}

const char *Data::FullName()
{
	return name;
}

const char *Data::ShortName()
{
	return FullName();
}

u_long Data::UniqueId()
{
	UpdateFields(eDataId);
	return uniqueid;
}

const char *Data::AsString()
{
	return ShortName();
}

int Data::Compare(Object *op)
{
	return StrCmp(ShortName(), Guard(op,Data)->ShortName(), -1, gStdEncoding->SortMap());
}

u_long Data::Hash()
{
	return strhash(FullName());
}

bool Data::IsEqual(Object *other)
{
	return other != 0 && IsA() == other->IsA()
		&& strcmp(FullName(), ((Data*)other)->FullName()) == 0;
}

StreamBuf *Data::GetStreamBuf(bool)
{
	AbstractMethod("GetStreamBuf");
	return 0;
}

StreamBuf *Data::GetStreamBufForReading()
{
	StreamBuf *sb= GetStreamBuf(FALSE);
	if (sb)
		sb->seek(0);
	return sb;
}

StreamBuf *Data::GetStreamBufForWriting()
{
	return GetStreamBuf(TRUE);
}

void Data::StartUpdate()
{
}

void Data::EndUpdate(bool, bool)
{
}

Object *Data::AsObject(Class *want)
{
	if (gTypes) {   // AsObject used in test mode (ala CanPaste)
		if (want)
			gTypes->AddFirst(want);
		return 0;
	}
	// AsObject used in normal mode (ala PasteData)
	return MakeObject(want);
}

Object *Data::MakeObject(Class *want)
{
	Converter *converter= Converter::FindConverter(this, want);
	if (converter && converter->CanConvert(this, want))
		return converter->Convert(this, want);
	return 0;
}

bool Data::CanConvert(Class *want)
{
	if (gTypes) {
		if (want)
			gTypes->AddFirst(want);
		return FALSE;
	}
	return Converter::FindConverter(this, want) != 0;
}

bool Data::CheckTypes(OrdCollection *typelist)
{
	if (typelist) {
		Iter next(typelist);
		Class *cl;
		
		SafeDelete(gConverters);
		gConverters= new OrdCollection;
		
		while (cl= (Class*) next())
			Converter::FindConverter(this, cl, gConverters);

		if (gConverters->Size() <= 0)
			SafeDelete(gConverters);
	}

	if (gConverters && gConverters->Size() > 0)
		return TRUE;
	return FALSE;
}

Bitmap *Data::GetBitmap()
{
	UpdateFields(eDataType);
	if (typematcher)
		return typematcher->GetBitmap();
	return gFileIcon;
}

SeqCollection *Data::GetContents()
{
	return 0;
}

bool Data::HasParent()
{
	Data *d= GetParent();
	bool b= (d != 0);
	SafeDelete(d);
	return b;
}

Data *Data::GetParent()
{
	return 0;
}

OStream& Data::PrintOn(OStream &s)
{
	RefCounted::PrintOn(s);
	s.PrintString(name);
	return s << bits SP << flags SP << creator SP << type SP
			<< sizehint SP << modtime SP << uniqueid SP
			<< (long)(classtype) SP
			<< (long)(typematcher) SP;
}

IStream& Data::ReadFrom(IStream &s)
{
	long ct, tm;
	RefCounted::ReadFrom(s);
	SafeDelete(name);
	s.ReadString(&name);
	s >> bits >> flags >> creator >> type >> sizehint >> modtime >> uniqueid >> ct >> tm;
	classtype= (Class*) ct;
	typematcher= (TypeMatcher*) tm;
	return s;
}

u_long Data::UpdateFields(u_long f)
{
	if (!TESTBIT(flags, f)) {
		switch (f) {
		case eDataType:
		case eDataDeepType:
		case eDataCreator:
			typematcher= TypeMatcher::MatchByContents(this);
			if (typematcher && type == cDocTypeUndef)
				type= typematcher->GetType();
			SETBIT(flags, eDataType);
			SETBIT(flags, eDataDeepType);
			SETBIT(flags, eDataCreator);
			break;
		}
	}
	return 0;
}

//---- MemoryData --------------------------------------------------------------

NewMetaImpl(MemoryData,Data, (TV(data,sizehint)));

MemoryData::MemoryData(byte *d, int l, const Symbol &t,
									bool copy, bool dd) : Data("MemoryData")
{
	type= t;
	sizehint= l;
	dodelete= dd;
	if (copy) {
		data= new byte[sizehint];
		MemCpy(data, d, (u_int)sizehint);
	} else
		data= d;

	SETBIT(flags, eDataType);
	SETBIT(flags, eDataDeepType);
	UpdateBit(eDataRead, TRUE);
}

MemoryData::~MemoryData()
{
	if (dodelete)
		SafeDelete(data);
}

StreamBuf *MemoryData::GetStreamBuf(bool forwriting)
{
	if (forwriting) {
		fprintf(stderr, "MemoryData::GetStreamBuf: no support for writing\n");
		return 0;
	}
	return new StreamBuf((int)sizehint, (char*) data, (int)sizehint, FALSE);
}

OStream& MemoryData::PrintOn(OStream &s)
{
	Data::PrintOn(s);
	return s.PrintString(data, (int)sizehint);
}

IStream& MemoryData::ReadFrom(IStream &s)
{
	Data::ReadFrom(s);
	SafeDelete(data);
	return s.ReadString(&data, (int*) &sizehint);
}

//---- StreamBufData -----------------------------------------------------------

NewMetaImpl0(StreamBufData,Data);

StreamBufData::StreamBufData(StreamBuf *s, const Symbol &t) : Data("StreamBufData")
{
	sb= s;
	type= t;
	SETBIT(flags, eDataType);
	SETBIT(flags, eDataDeepType);
	if (sb)
		sizehint= sb->tell();
	else
		sizehint= -1;
}

StreamBuf *StreamBufData::GetStreamBuf(bool)
{
	return sb;
}

//---- ObjectData --------------------------------------------------------------

NewMetaImpl(ObjectData,Data, (TP(object)));

ObjectData::ObjectData(Object *op) : Data("ObjectData")
{
	object= op;
	typematcher= gEtMatcher;
	type= cDocTypeET;
	if (object)
		classtype= object->IsA();
	SETBIT(flags, eDataType);
	SETBIT(flags, eDataDeepType);
	SETBIT(flags, eDataCreator);
	UpdateBit(eDataRead, TRUE);
}

ObjectData::~ObjectData()
{
	SafeDelete(object);
}

Object *ObjectData::MakeObject(Class *want)
{
	if (GetClassType()->isKindOf(want))
		return object->DeepClone();
	Converter *converter= Converter::FindConverter(this, want);
	if (converter)
		return converter->Convert(this, want);
	return 0;
}

bool ObjectData::CanConvert(Class *want)
{
	if (gTypes)
		return Data::CanConvert(want);
	if (GetClassType()->isKindOf(want))
		return TRUE;
	if (Converter::FindConverter(this, want))
		return TRUE;
	return FALSE;
}

OStream& ObjectData::PrintOn(OStream &s)
{
	Data::PrintOn(s);
	return s << object;
}

IStream& ObjectData::ReadFrom(IStream &s)
{
	Data::ReadFrom(s);
	return s >> object;
}

